
#pragma once

namespace atr {

/**
 * @brief Namespace only for internal use of atr library
 * @details Please don't use this namespace outside the atr library. IF you REALLY need to - do it with special care.
 */
namespace __ {

template <template <class, class> class BinaryPredicate,
          class Prevs, class Current, class Nexts, bool isGood>
    struct __unique;

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base, class Dummy, bool isGoodDummy>
struct __unique<BinaryPredicate, Base<>, Dummy, Base<>, isGoodDummy>
    { using type = Base<>; };

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class ... Prevs, class Current, class Next>
struct __unique<BinaryPredicate, Base<Prevs...>, Current, Base<Next>, true>
    { using type = Base<Prevs..., Current, Next>; };

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class ... Prevs, class Current, class Next>
struct __unique<BinaryPredicate, Base<Prevs...>, Current, Base<Next>, false>
    { using type = Base<Prevs..., Current>; };

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class ... Prevs,
          class Current,
          class First, class Second, class ... Nexts>
struct __unique<BinaryPredicate,
                Base<Prevs...>,
                Current,
                Base<First, Second, Nexts...>,
                false>
    : public __unique<BinaryPredicate,
                      Base<Prevs...>,
                      Current,
                      Base<Second, Nexts...>,
                      !BinaryPredicate<Current, Second>::type::value> {};

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class ... Prevs,
          class Current,
          class First, class Second, class ... Nexts>
struct __unique<BinaryPredicate,
                Base<Prevs...>,
                Current,
                Base<First, Second, Nexts...>,
                true>
    : public __unique<BinaryPredicate,
                      Base<Prevs..., Current>,
                      First,
                      Base<Second, Nexts...>,
                      !BinaryPredicate<First, Second>::type::value> {};

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class First, class Second, class ... Other>
struct __unique<BinaryPredicate,
                Base<>,
                bool,
                Base<First, Second, Other...>,
                false>
    : public __unique<BinaryPredicate,
                      Base<>,
                      First,
                      Base<Second, Other...>,
                      !BinaryPredicate<First, Second>::type::value> {};

template <template <class, class> class BinaryPredicate,
          template <class ...> class Base,
          class Only>
struct __unique<BinaryPredicate,
                Base<>,
                bool,
                Base<Only>,
                false>
    { using type = Base<Only>; };

} // namespace __
} // namespace atr
